package com.mc.fastkit.view.shape

import android.content.Context
import android.content.res.ColorStateList
import android.content.res.TypedArray
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Typeface
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.StateListDrawable
import android.text.InputFilter.LengthFilter
import android.text.TextUtils
import android.text.method.ScrollingMovementMethod
import android.util.AttributeSet
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.annotation.FontRes
import androidx.annotation.GravityInt
import androidx.annotation.IntDef
import androidx.annotation.StringRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.res.ResourcesCompat
import androidx.core.content.withStyledAttributes
import androidx.core.graphics.drawable.toDrawable
import androidx.core.view.isGone
import com.mc.fastkit.R
import com.mc.fastkit.ext.cast
import com.mc.fastkit.ext.constraintLayoutParams
import com.mc.fastkit.ext.dp2px
import com.mc.fastkit.ext.getDrawableStrict
import com.mc.fastkit.ext.setBottomMargin
import com.mc.fastkit.ext.setDrawable
import com.mc.fastkit.ext.setEndMargin
import com.mc.fastkit.ext.setHeight
import com.mc.fastkit.ext.setMargins
import com.mc.fastkit.ext.setPaddingBottom
import com.mc.fastkit.ext.setPaddingEnd
import com.mc.fastkit.ext.setPaddingStart
import com.mc.fastkit.ext.setPaddingTop
import com.mc.fastkit.ext.setSize
import com.mc.fastkit.ext.setStartMargin
import com.mc.fastkit.ext.setTopMargin
import com.mc.fastkit.ext.setWidth
import com.mc.fastkit.ext.takeNotNull
import com.mc.fastkit.view.StateType

/**
 * 包含Image的TextView
 * @author: MasterChan
 * @date: 2023-03-27 13:09:08
 */
open class ShapedLabelView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0,
) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes),
    IShapedView by ShapedViewDelegate(context, attrs) {

    val imageView = ImageView(context)
    val textView = TextView(context)
    private val dividerView = TextView(context)

    var imageNormal: Drawable? = null
        private set
    var imageState: Drawable? = null
        private set
    var imageDisable: Drawable? = null
        private set
    var imageWidth: Int = -2
        private set
    var imageHeight: Int = -2
        private set
    var imageGravity = ImageGravity.START
        private set
    var imageSpacing: Int = 0
        private set
    var imageMarginStart: Int = 0
        private set
    var imageMarginTop: Int = 0
        private set
    var imageMarginEnd: Int = 0
        private set
    var imageMarginBottom: Int = 0
        private set
    var textWidth: Int = -2
        private set
    var textHeight: Int = -2
        private set
    var textHideWhenEmpty = true
        private set
    var textColorNormal: Int = 0
        private set
    var textColorState: Int = 0
        private set
    var textColorDisable: Int = 0
        private set
    var imageHorizontalBias: Float = 0.5f
        private set
    var imageVerticalBias: Float = 0.5f
        private set
    var imageWidthWeight = 0f
        private set
    var imageHeightWeight = 0f
        private set
    var imageWidthMin = 0
        private set
    var imageWidthMax = 0
        private set
    var imageHeightMin = 0
        private set
    var imageHeightMax = 0
        private set
    var textHorizontalBias: Float = 0.5f
        private set
    var textVerticalBias: Float = 0.5f
        private set
    var textWidthWeight = 0f
        private set
    var textHeightWeight = 0f
        private set
    var textWidthMin = 0
        private set
    var textWidthMax = 0
        private set
    var textHeightMin = 0
        private set
    var textHeightMax = 0
        private set

    @IntDef(
        ImageGravity.START, ImageGravity.TOP, ImageGravity.END, ImageGravity.BOTTOM,
        ImageGravity.CENTER
    )
    @Retention(AnnotationRetention.SOURCE)
    annotation class ImageGravity {
        companion object {
            const val START = 0
            const val TOP = 1
            const val END = 2
            const val BOTTOM = 3
            const val CENTER = 4
        }
    }

    init {
        this.init()
        context.withStyledAttributes(attrs, R.styleable.ShapedLabelView) {
            obtainImageAttrs(this)
            obtainTextAttrs(this)
            obtainDivider(this)
        }
        setImageGravity(imageGravity)
    }

    private fun obtainImageAttrs(a: TypedArray) {
        imageNormal = a.getDrawable(R.styleable.ShapedLabelView_mc_image)
        imageState = a.getDrawable(R.styleable.ShapedLabelView_mc_imageState)
        imageDisable = a.getDrawable(R.styleable.ShapedLabelView_mc_imageDisable)
        val adjustViewBounds = a.getBoolean(R.styleable.ShapedLabelView_mc_adjustViewBounds, false)
        val scaleType = a.getInteger(R.styleable.ShapedLabelView_mc_scaleType, 6)
        imageWidth = a.getLayoutDimension(R.styleable.ShapedLabelView_mc_imageWidth, -2)
        imageHeight = a.getLayoutDimension(R.styleable.ShapedLabelView_mc_imageHeight, -2)
        imageGravity = a.getInteger(R.styleable.ShapedLabelView_mc_imageGravity, ImageGravity.START)
        imageSpacing = a.getDimensionPixelOffset(R.styleable.ShapedLabelView_mc_imageSpacing, 0)
        imageMarginStart = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_imageMarginStart, 0
        )
        imageMarginTop = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_imageMarginTop, 0
        )
        imageMarginEnd = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_imageMarginEnd, 0
        )
        imageMarginBottom = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_imageMarginBottom, 0
        )
        imageHorizontalBias = a.getFloat(R.styleable.ShapedLabelView_mc_imageHorizontal_bias, 0.5f)
        imageVerticalBias = a.getFloat(R.styleable.ShapedLabelView_mc_imageVertical_bias, 0.5f)
        imageWidthWeight = a.getFloat(R.styleable.ShapedLabelView_mc_imageWidth_weight, -1f)
        imageHeightWeight = a.getFloat(R.styleable.ShapedLabelView_mc_imageHeight_weight, -1f)
        imageWidthMin = a.getDimensionPixelOffset(R.styleable.ShapedLabelView_mc_imageWidth_min, 0)
        imageWidthMax = a.getDimensionPixelOffset(R.styleable.ShapedLabelView_mc_imageWidth_max, 0)
        imageHeightMin = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_imageHeight_min, 0
        )
        imageHeightMax = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_imageHeight_max, 0
        )

        setImageResource(imageNormal, imageState, imageDisable)
        setAdjustViewBounds(adjustViewBounds)
        setScaleType(ImageView.ScaleType.entries[scaleType])
        setImageSize(imageWidth, imageHeight)
        setImageMargin(imageMarginStart, imageMarginTop, imageMarginEnd, imageMarginBottom)
    }

    private fun obtainTextAttrs(a: TypedArray) {
        val text = a.getString(R.styleable.ShapedLabelView_mc_text) ?: ""
        textWidth = a.getLayoutDimension(R.styleable.ShapedLabelView_mc_textWidth, -2)
        textHeight = a.getLayoutDimension(R.styleable.ShapedLabelView_mc_textHeight, -2)
        textColorNormal = a.getColor(R.styleable.ShapedLabelView_mc_textColor, Color.BLACK)
        textColorState = a.getColor(R.styleable.ShapedLabelView_mc_textColorState, textColorNormal)
        textColorDisable = a.getColor(
            R.styleable.ShapedLabelView_mc_textColorDisable, textColorNormal
        )
        textHideWhenEmpty = a.getBoolean(R.styleable.ShapedLabelView_mc_textHideWhenEmpty, true)
        textHorizontalBias = a.getFloat(R.styleable.ShapedLabelView_mc_textHorizontal_bias, 0.5f)
        textVerticalBias = a.getFloat(R.styleable.ShapedLabelView_mc_textVertical_bias, 0.5f)
        textWidthWeight = a.getFloat(R.styleable.ShapedLabelView_mc_textWidth_weight, -1f)
        textHeightWeight = a.getFloat(R.styleable.ShapedLabelView_mc_textHeight_weight, -1f)
        textWidthMin = a.getDimensionPixelOffset(R.styleable.ShapedLabelView_mc_textWidth_min, 0)
        textWidthMax = a.getDimensionPixelOffset(R.styleable.ShapedLabelView_mc_textWidth_max, 0)
        textHeightMin = a.getDimensionPixelOffset(R.styleable.ShapedLabelView_mc_textHeight_min, 0)
        textHeightMax = a.getDimensionPixelOffset(R.styleable.ShapedLabelView_mc_textHeight_max, 0)
        val textSize = a.getDimension(R.styleable.ShapedLabelView_mc_textSize, context.dp2px(14))
        val maxLength = a.getInteger(R.styleable.ShapedLabelView_mc_maxLength, -1)
        val textStyle = a.getInteger(R.styleable.ShapedLabelView_mc_textStyle, 0)
        val textFont = a.getResourceId(R.styleable.ShapedLabelView_mc_font, 0)
        val textBackground = a.getDrawable(R.styleable.ShapedLabelView_mc_textBackground)
        val textGravity = a.getInteger(R.styleable.ShapedLabelView_mc_textGravity, Gravity.START)
        val maxLines = a.getInteger(R.styleable.ShapedLabelView_mc_maxLines, -1)
        val ellipsize = a.getInteger(R.styleable.ShapedLabelView_mc_ellipsize, 0).toEllipsize()
        val textScrollable = a.getBoolean(R.styleable.ShapedLabelView_mc_textScrollable, false)
        val ems = a.getInteger(R.styleable.ShapedLabelView_mc_ems, -1)
        val textPadding = a.getDimensionPixelOffset(R.styleable.ShapedLabelView_mc_textPadding, 0)
        val textPaddingStart = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_textPaddingStart, textPadding
        )
        val textPaddingTop = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_textPaddingTop, textPadding
        )
        val textPaddingEnd = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_textPaddingEnd, textPadding
        )
        val textPaddingBottom = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_textPaddingBottom, textPadding
        )
        val drawableStart = a.getDrawable(R.styleable.ShapedLabelView_mc_drawableStart)
        val drawableStartWidth = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableStart_width, -1
        )
        val drawableStartHeight = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableStart_height, -1
        )
        val drawableStartPadding = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableStart_padding, 0
        )
        val drawableStartPaddingStart = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableStart_paddingStart, drawableStartPadding
        )
        val drawableStartPaddingTop = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableStart_paddingTop, drawableStartPadding
        )
        val drawableStartPaddingEnd = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableStart_paddingEnd, drawableStartPadding
        )
        val drawableStartPaddingBottom = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableStart_paddingBottom, drawableStartPadding
        )
        val drawableTop = a.getDrawable(R.styleable.ShapedLabelView_mc_drawableTop)
        val drawableTopWidth = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableTop_width, -1
        )
        val drawableTopHeight = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableTop_height, -1
        )
        val drawableTopPadding = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableTop_padding, 0
        )
        val drawableTopPaddingStart = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableTop_paddingStart, drawableTopPadding
        )
        val drawableTopPaddingTop = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableTop_paddingTop, drawableTopPadding
        )
        val drawableTopPaddingEnd = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableTop_paddingEnd, drawableTopPadding
        )
        val drawableTopPaddingBottom = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableTop_paddingBottom, drawableTopPadding
        )
        val drawableEnd = a.getDrawable(R.styleable.ShapedLabelView_mc_drawableEnd)
        val drawableEndWidth = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableEnd_width, -1
        )
        val drawableEndHeight = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableEnd_height, -1
        )
        val drawableEndPadding = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableEnd_padding, 0
        )
        val drawableEndPaddingStart = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableEnd_paddingStart, drawableEndPadding
        )
        val drawableEndPaddingTop = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableEnd_paddingTop, drawableEndPadding
        )
        val drawableEndPaddingEnd = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableEnd_paddingEnd, drawableEndPadding
        )
        val drawableEndPaddingBottom = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableEnd_paddingBottom, drawableEndPadding
        )
        val drawableBottom = a.getDrawable(R.styleable.ShapedLabelView_mc_drawableBottom)
        val drawableBottomWidth = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableBottom_width, -1
        )
        val drawableBottomHeight = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableBottom_height, -1
        )
        val drawableBottomPadding = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableBottom_padding, 0
        )
        val drawableBottomPaddingStart = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableBottom_paddingStart, drawableBottomPadding
        )
        val drawableBottomPaddingTop = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableBottom_paddingTop, drawableBottomPadding
        )
        val drawableBottomPaddingEnd = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableBottom_paddingEnd, drawableBottomPadding
        )
        val drawableBottomPaddingBottom = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_drawableBottom_paddingBottom, drawableBottomPadding
        )
        setText(text)
        textView.setSize(textWidth, textHeight)
        setTextStyle(textStyle, textFont)
        setTextSize(textSize)
        if (maxLength >= 0) {
            setMaxLength(maxLength)
        }
        setTextColor(textColorNormal, textColorState, textColorDisable)
        setTextBackground(textBackground)
        setTextPadding(textPaddingStart, textPaddingTop, textPaddingEnd, textPaddingBottom)
        setTextGravity(textGravity)
        setTextScrollAble(textScrollable)
        if (ems >= 0) {
            setEms(ems)
        }
        if (maxLines >= 0) {
            setMaxLines(maxLines)
        }
        setEllipsize(ellipsize)
        val startDrawable = drawableStart.takeNotNull {
            createPaddingDrawable(
                it, drawableStartWidth, drawableStartHeight, drawableStartPaddingStart,
                drawableStartPaddingTop, drawableStartPaddingEnd, drawableStartPaddingBottom
            )
        }
        val topDrawable = drawableTop.takeNotNull {
            createPaddingDrawable(
                it, drawableTopWidth, drawableTopHeight, drawableTopPaddingStart,
                drawableTopPaddingTop, drawableTopPaddingEnd, drawableTopPaddingBottom
            )
        }
        val endDrawable = drawableEnd.takeNotNull {
            createPaddingDrawable(
                it, drawableEndWidth, drawableEndHeight, drawableEndPaddingStart,
                drawableEndPaddingTop, drawableEndPaddingEnd, drawableEndPaddingBottom
            )
        }
        val bottomDrawable = drawableBottom.takeNotNull {
            createPaddingDrawable(
                it, drawableBottomWidth, drawableBottomHeight, drawableBottomPaddingStart,
                drawableBottomPaddingTop, drawableBottomPaddingEnd, drawableBottomPaddingBottom
            )
        }
        textView.setCompoundDrawables(startDrawable, topDrawable, endDrawable, bottomDrawable)
    }

    private fun obtainDivider(a: TypedArray) {
        val divider = a.getDrawable(R.styleable.ShapedLabelView_mc_divider)
        val dividerHeight = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_dividerHeight, 0
        )
        val dividerColor = a.getColor(R.styleable.ShapedLabelView_mc_dividerColor, Color.BLACK)
        val dividerMarginStart = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_dividerMarginStart, 0
        )
        val dividerMarginTop = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_dividerMarginTop, 0
        )
        val dividerMarginEnd = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_dividerMarginEnd, 0
        )
        val dividerMarginBottom = a.getDimensionPixelOffset(
            R.styleable.ShapedLabelView_mc_dividerMarginBottom, 0
        )
        val layoutParams = LayoutParams(0, dividerHeight)
        layoutParams.marginStart = dividerMarginStart
        layoutParams.topMargin = dividerMarginTop
        layoutParams.marginEnd = dividerMarginEnd
        layoutParams.bottomMargin = dividerMarginBottom
        if (divider != null) {
            dividerView.background = divider
        } else {
            dividerView.setBackgroundColor(dividerColor)
        }
        dividerView.layoutParams = layoutParams
    }

    protected open fun init() {
        textView.isFocusable = false
        imageView.id = View.generateViewId()
        textView.id = View.generateViewId()
        dividerView.id = View.generateViewId()
        addView(dividerView)
        addView(imageView)
        addView(textView)
        into(this)
    }

    override fun setEnabled(enabled: Boolean) {
        super.setEnabled(enabled)
        textView.isEnabled = enabled
        imageView.isEnabled = enabled
    }

    fun setImageBitmap(bitmap: Bitmap?) = apply {
        setImageResource(bitmap?.toDrawable(resources), imageState, imageDisable)
    }

    fun setImageResource(@DrawableRes res: Int) = apply {
        setImageResource(getDrawableStrict(res), imageState, imageDisable)
    }

    fun setImageDrawable(drawable: Drawable?) = apply {
        setImageResource(drawable, imageState, imageDisable)
    }

    /**
     * 设置带状态的Image，可使用[stateType]切换状态类型
     * @param normalImage 正常状态的图片
     * @param stateImage [stateType]下的图片
     * @param disableImage [setEnabled]为false时的图片
     * @return ShapedLabelView
     */
    fun setImageResource(
        normalImage: Drawable?,
        stateImage: Drawable?,
        disableImage: Drawable?
    ) = apply {
        this.imageNormal = normalImage
        this.imageState = stateImage
        this.imageDisable = disableImage
        val state = stateType.androidState
        val drawable = StateListDrawable()
        drawable.addState(intArrayOf(android.R.attr.state_enabled, -state), normalImage)
        drawable.addState(
            intArrayOf(android.R.attr.state_enabled, state),
            if (stateType == StateType.NONE) normalImage else stateImage
        )
        drawable.addState(intArrayOf(-android.R.attr.state_enabled), disableImage)
        imageView.setImageDrawable(drawable)
    }

    fun setAdjustViewBounds(adjustViewBounds: Boolean) = apply {
        imageView.adjustViewBounds = adjustViewBounds
    }

    fun setScaleType(scaleType: ImageView.ScaleType) = apply {
        imageView.scaleType = scaleType
    }

    fun setImageWidth(width: Int) = apply {
        imageView.setWidth(width)
    }

    fun setImageHeight(height: Int) = apply {
        imageView.setHeight(height)
    }

    fun setImageSize(width: Int, height: Int) = apply {
        imageView.setSize(width, height)
    }

    fun setImageHorizontalBias(bias: Float) = apply {
        imageHorizontalBias = bias
        setImageGravity(imageGravity)
    }

    fun setImageVerticalBias(bias: Float) = apply {
        imageVerticalBias = bias
        setImageGravity(imageGravity)
    }

    fun setImageWidthWeight(weight: Float) = apply {
        imageWidthWeight = weight
        setImageGravity(imageGravity)
    }

    fun setImageHeightWeight(weight: Float) = apply {
        imageHeightWeight = weight
        setImageGravity(imageGravity)
    }

    fun setImageWidthMin(minWidth: Int) = apply {
        imageWidthMin = minWidth
        setImageGravity(imageGravity)
    }

    fun setImageWidthMax(maxWidth: Int) = apply {
        imageWidthMax = maxWidth
        setImageGravity(imageGravity)
    }

    fun setImageHeightMin(minHeight: Int) = apply {
        imageHeightMin = minHeight
        setImageGravity(imageGravity)
    }

    fun setImageHeightMax(maxHeight: Int) = apply {
        imageHeightMax = maxHeight
        setImageGravity(imageGravity)
    }

    fun setImageSpacing(padding: Int) = apply {
        val layoutParams = textView.layoutParams as MarginLayoutParams
        when (imageGravity) {
            ImageGravity.START -> layoutParams.setMargins(padding, 0, 0, 0)
            ImageGravity.TOP -> layoutParams.setMargins(0, padding, 0, 0)
            ImageGravity.END -> layoutParams.setMargins(0, 0, padding, 0)
            ImageGravity.BOTTOM -> layoutParams.setMargins(0, 0, 0, padding)
        }
        textView.layoutParams = layoutParams
    }

    fun setImageMargin(margin: Int) = apply {
        imageMarginStart = margin
        imageMarginTop = margin
        imageMarginEnd = margin
        imageMarginBottom = margin
        imageView.setMargins(margin)
    }

    fun setImageMarginStart(margin: Int) = apply {
        imageMarginStart = margin
        imageView.setStartMargin(margin)
    }

    fun setImageMarginTop(margin: Int) = apply {
        imageMarginTop = margin
        imageView.setTopMargin(margin)
    }

    fun setImageMarginEnd(margin: Int) = apply {
        imageMarginEnd = margin
        imageView.setEndMargin(margin)
    }

    fun setImageMarginBottom(margin: Int) = apply {
        imageMarginBottom = margin
        imageView.setBottomMargin(margin)
    }

    fun setImageMargin(
        startMargin: Int,
        topMargin: Int,
        endMargin: Int,
        bottomMargin: Int
    ) = apply {
        val params = imageView.layoutParams.cast<MarginLayoutParams>()
        params.setMargins(startMargin, topMargin, endMargin, bottomMargin)
        imageView.layoutParams = params
    }

    fun setImageGravity(@ImageGravity imageGravity: Int) = apply {
        this.imageGravity = imageGravity
        removeAllViews()
        val imageParams = LayoutParams(imageWidth, imageHeight)
        val textParams = LayoutParams(textWidth, textHeight)
        val dividerParams = dividerView.constraintLayoutParams
        dividerParams.reset()
        when (imageGravity) {
            ImageGravity.START -> {
                imageParams.startToStart = LayoutParams.PARENT_ID
                imageParams.endToStart = textView.id
                imageParams.topToTop = LayoutParams.PARENT_ID
                imageParams.bottomToBottom = LayoutParams.PARENT_ID
                imageParams.horizontalChainStyle = LayoutParams.CHAIN_PACKED
                imageParams.verticalBias = imageVerticalBias
                imageParams.horizontalBias = imageHorizontalBias
                imageParams.horizontalWeight = imageWidthWeight
                imageParams.verticalWeight = imageHeightWeight
                imageParams.matchConstraintMinWidth = imageWidthMin
                imageParams.matchConstraintMaxWidth = imageWidthMax
                imageParams.matchConstraintMinHeight = imageHeightMin
                imageParams.matchConstraintMaxHeight = imageHeightMax

                textParams.startToEnd = imageView.id
                textParams.endToEnd = LayoutParams.PARENT_ID
                textParams.topToTop = LayoutParams.PARENT_ID
                textParams.bottomToBottom = LayoutParams.PARENT_ID
                textParams.verticalBias = textVerticalBias
                textParams.horizontalBias = textHorizontalBias
                textParams.horizontalWeight = textWidthWeight
                textParams.verticalWeight = textHeightWeight
                textParams.matchConstraintMinWidth = textWidthMin
                textParams.matchConstraintMaxWidth = textWidthMax
                textParams.matchConstraintMinHeight = textHeightMin
                textParams.matchConstraintMaxHeight = textHeightMax
                textParams.constrainedWidth = true

                dividerParams.startToStart = LayoutParams.PARENT_ID
                dividerParams.endToEnd = LayoutParams.PARENT_ID
                dividerParams.bottomToBottom = LayoutParams.PARENT_ID
            }

            ImageGravity.TOP -> {
                imageParams.startToStart = LayoutParams.PARENT_ID
                imageParams.endToEnd = LayoutParams.PARENT_ID
                imageParams.topToTop = LayoutParams.PARENT_ID
                imageParams.bottomToTop = textView.id
                imageParams.verticalChainStyle = LayoutParams.CHAIN_PACKED
                imageParams.verticalBias = imageVerticalBias
                imageParams.horizontalBias = imageHorizontalBias
                imageParams.horizontalWeight = imageWidthWeight
                imageParams.verticalWeight = imageHeightWeight
                imageParams.matchConstraintMinWidth = imageWidthMin
                imageParams.matchConstraintMaxWidth = imageWidthMax
                imageParams.matchConstraintMinHeight = imageHeightMin
                imageParams.matchConstraintMaxHeight = imageHeightMax

                textParams.startToStart = LayoutParams.PARENT_ID
                textParams.endToEnd = LayoutParams.PARENT_ID
                textParams.topToBottom = imageView.id
                textParams.bottomToBottom = LayoutParams.PARENT_ID
                textParams.verticalBias = textVerticalBias
                textParams.horizontalBias = textHorizontalBias
                textParams.horizontalWeight = textWidthWeight
                textParams.verticalWeight = textHeightWeight
                textParams.matchConstraintMinWidth = textWidthMin
                textParams.matchConstraintMaxWidth = textWidthMax
                textParams.matchConstraintMinHeight = textHeightMin
                textParams.matchConstraintMaxHeight = textHeightMax
                textParams.constrainedHeight = true

                dividerParams.startToStart = LayoutParams.PARENT_ID
                dividerParams.endToEnd = LayoutParams.PARENT_ID
                dividerParams.bottomToBottom = LayoutParams.PARENT_ID
            }

            ImageGravity.END -> {
                textParams.startToStart = LayoutParams.PARENT_ID
                textParams.endToStart = imageView.id
                textParams.topToTop = LayoutParams.PARENT_ID
                textParams.bottomToBottom = LayoutParams.PARENT_ID
                textParams.verticalBias = textVerticalBias
                textParams.horizontalBias = textHorizontalBias
                textParams.horizontalChainStyle = LayoutParams.CHAIN_PACKED
                textParams.horizontalWeight = textWidthWeight
                textParams.verticalWeight = textHeightWeight
                textParams.matchConstraintMinWidth = textWidthMin
                textParams.matchConstraintMaxWidth = textWidthMax
                textParams.matchConstraintMinHeight = textHeightMin
                textParams.matchConstraintMaxHeight = textHeightMax
                textParams.constrainedWidth = true

                imageParams.startToEnd = textView.id
                imageParams.endToEnd = LayoutParams.PARENT_ID
                imageParams.topToTop = LayoutParams.PARENT_ID
                imageParams.bottomToBottom = LayoutParams.PARENT_ID
                imageParams.verticalBias = imageVerticalBias
                imageParams.horizontalBias = imageHorizontalBias
                imageParams.horizontalWeight = imageWidthWeight
                imageParams.verticalWeight = imageHeightWeight
                imageParams.matchConstraintMinWidth = imageWidthMin
                imageParams.matchConstraintMaxWidth = imageWidthMax
                imageParams.matchConstraintMinHeight = imageHeightMin
                imageParams.matchConstraintMaxHeight = imageHeightMax

                dividerParams.startToStart = LayoutParams.PARENT_ID
                dividerParams.endToEnd = LayoutParams.PARENT_ID
                dividerParams.bottomToBottom = LayoutParams.PARENT_ID
            }

            ImageGravity.BOTTOM -> {
                textParams.startToStart = LayoutParams.PARENT_ID
                textParams.endToEnd = LayoutParams.PARENT_ID
                textParams.topToTop = LayoutParams.PARENT_ID
                textParams.bottomToTop = imageView.id
                textParams.verticalBias = textVerticalBias
                textParams.horizontalBias = textHorizontalBias
                textParams.verticalChainStyle = LayoutParams.CHAIN_PACKED
                textParams.horizontalWeight = textWidthWeight
                textParams.verticalWeight = textHeightWeight
                textParams.matchConstraintMinWidth = textWidthMin
                textParams.matchConstraintMaxWidth = textWidthMax
                textParams.matchConstraintMinHeight = textHeightMin
                textParams.matchConstraintMaxHeight = textHeightMax

                imageParams.startToStart = LayoutParams.PARENT_ID
                imageParams.endToEnd = LayoutParams.PARENT_ID
                imageParams.topToBottom = textView.id
                imageParams.bottomToBottom = LayoutParams.PARENT_ID
                imageParams.verticalBias = imageVerticalBias
                imageParams.horizontalBias = imageHorizontalBias
                imageParams.horizontalWeight = imageWidthWeight
                imageParams.verticalWeight = imageHeightWeight
                imageParams.matchConstraintMinWidth = imageWidthMin
                imageParams.matchConstraintMaxWidth = imageWidthMax
                imageParams.matchConstraintMinHeight = imageHeightMin
                imageParams.matchConstraintMaxHeight = imageHeightMax
                textParams.constrainedHeight = true

                dividerParams.startToStart = LayoutParams.PARENT_ID
                dividerParams.endToEnd = LayoutParams.PARENT_ID
                dividerParams.bottomToBottom = LayoutParams.PARENT_ID
            }

            ImageGravity.CENTER -> {
                imageParams.startToStart = LayoutParams.PARENT_ID
                imageParams.endToEnd = LayoutParams.PARENT_ID
                imageParams.topToTop = LayoutParams.PARENT_ID
                imageParams.bottomToBottom = LayoutParams.PARENT_ID
                imageParams.verticalBias = imageVerticalBias
                imageParams.horizontalBias = imageHorizontalBias
                imageParams.horizontalWeight = imageWidthWeight
                imageParams.verticalWeight = imageHeightWeight
                imageParams.matchConstraintMinWidth = imageWidthMin
                imageParams.matchConstraintMaxWidth = imageWidthMax
                imageParams.matchConstraintMinHeight = imageHeightMin
                imageParams.matchConstraintMaxHeight = imageHeightMax

                textParams.startToStart = LayoutParams.PARENT_ID
                textParams.endToEnd = LayoutParams.PARENT_ID
                textParams.topToTop = LayoutParams.PARENT_ID
                textParams.bottomToBottom = LayoutParams.PARENT_ID
                textParams.verticalBias = textVerticalBias
                textParams.horizontalBias = textHorizontalBias
                textParams.horizontalWeight = textWidthWeight
                textParams.verticalWeight = textHeightWeight
                textParams.matchConstraintMinWidth = textWidthMin
                textParams.matchConstraintMaxWidth = textWidthMax
                textParams.matchConstraintMinHeight = textHeightMin
                textParams.matchConstraintMaxHeight = textHeightMax

                dividerParams.startToStart = LayoutParams.PARENT_ID
                dividerParams.endToEnd = LayoutParams.PARENT_ID
                dividerParams.bottomToBottom = LayoutParams.PARENT_ID
            }
        }
        addView(dividerView, dividerParams)
        addView(imageView, imageParams)
        addView(textView, textParams)
        setImageSpacing(imageSpacing)
        setImageMargin(imageMarginStart, imageMarginTop, imageMarginEnd, imageMarginBottom)
    }

    fun setText(text: CharSequence) = apply {
        textView.isGone = textHideWhenEmpty && text.isEmpty()
        textView.text = text
    }

    fun setText(@StringRes text: Int) = apply {
        setText(context.getString(text))
    }

    fun setTextWidth(width: Int) = apply {
        if (textWidth == width) {
            return@apply
        }
        textWidth = width
        val lp = textView.layoutParams
        lp.width = width
        textView.layoutParams = lp
    }

    fun setTextHeight(height: Int) = apply {
        if (textHeight == height) {
            return@apply
        }
        textHeight = height
        val lp = textView.layoutParams
        lp.height = height
        textView.layoutParams = lp
    }

    fun setTextHorizontalBias(bias: Float) = apply {
        textHorizontalBias = bias
        setImageGravity(imageGravity)
    }

    fun setTextVerticalBias(bias: Float) = apply {
        textVerticalBias = bias
        setImageGravity(imageGravity)
    }

    fun setTextWidthWeight(weight: Float) = apply {
        textWidthWeight = weight
        setImageGravity(imageGravity)
    }

    fun setTextHeightWeight(weight: Float) = apply {
        textHeightWeight = weight
        setImageGravity(imageGravity)
    }

    fun setTextWidthMin(minWidth: Int) = apply {
        textWidthMin = minWidth
        setImageGravity(imageGravity)
    }

    fun setTextWidthMax(maxWidth: Int) = apply {
        textWidthMax = maxWidth
        setImageGravity(imageGravity)
    }

    fun setTextHeightMin(minHeight: Int) = apply {
        textHeightMin = minHeight
        setImageGravity(imageGravity)
    }

    fun setTextHeightMax(maxHeight: Int) = apply {
        textHeightMax = maxHeight
        setImageGravity(imageGravity)
    }

    fun setTextStyle(textStyle: Int) = apply {
        setTextStyle(textStyle, null)
    }

    fun setTextStyle(textStyle: Int, @FontRes fontRes: Int) = apply {
        val font = if (fontRes == 0) null else ResourcesCompat.getFont(context, fontRes)
        setTextStyle(textStyle, font)
    }

    fun setTextStyle(textStyle: Int, typeface: Typeface?) = apply {
        textView.setTypeface(typeface, textStyle)
    }

    fun setTextColor(@ColorInt color: Int) = apply {
        setTextColor(color, textColorState, textColorDisable)
    }

    fun setTextColor(
        @ColorInt normalColor: Int,
        @ColorInt stateColor: Int,
        @ColorInt disableColor: Int
    ) {
        this.textColorState = stateColor
        this.textColorDisable = disableColor
        val states = mutableListOf<List<Int>>()
        val colors = mutableListOf<Int>()
        if (textColorState != 0 && stateType != StateType.NONE) {
            states.add(listOf(android.R.attr.state_enabled, -stateType.androidState))
            colors.add(normalColor)
            states.add(listOf(android.R.attr.state_enabled, stateType.androidState))
            colors.add(textColorState)
        } else {
            states.add(listOf(android.R.attr.state_enabled))
            colors.add(normalColor)
        }
        if (this.textColorDisable == 0) {
            this.textColorDisable = normalColor
        }
        states.add(listOf(-android.R.attr.state_enabled))
        colors.add(textColorDisable)
        textView.setTextColor(
            ColorStateList(states.map { it.toIntArray() }.toTypedArray(), colors.toIntArray())
        )
    }

    fun setTextSize(textSize: Float) = apply {
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
    }

    fun setMaxLength(maxLength: Int) = apply {
        textView.filters = arrayOf(LengthFilter(maxLength))
    }

    fun setTextBackground(drawable: Drawable?) = apply {
        textView.background = drawable
    }

    fun setTextBackground(@DrawableRes res: Int) = apply {
        textView.setBackgroundResource(res)
    }

    fun setTextBackground(bitmap: Bitmap?) = apply {
        textView.background = BitmapDrawable(resources, bitmap)
    }

    fun setTextStartPadding(padding: Int) = apply {
        textView.setPaddingStart(padding)
    }

    fun setTextTopPadding(padding: Int) = apply {
        textView.setPaddingTop(padding)
    }

    fun setTextEndPadding(padding: Int) = apply {
        textView.setPaddingEnd(padding)
    }

    fun setTextBottomPadding(padding: Int) = apply {
        textView.setPaddingBottom(padding)
    }

    fun setTextPadding(start: Int, top: Int, end: Int, bottom: Int) = apply {
        textView.setPaddingRelative(start, top, end, bottom)
    }

    fun setTextGravity(@GravityInt gravity: Int) = apply {
        textView.gravity = gravity
    }

    /**
     * 设置TextView是否可以滚动
     * @param scrollAble Boolean
     * @return ShapedLabelView
     */
    fun setTextScrollAble(scrollAble: Boolean) = apply {
        textView.movementMethod = if (scrollAble) ScrollingMovementMethod.getInstance() else null
    }

    fun setEms(ems: Int) = apply {
        textView.setEms(ems)
    }

    fun setMaxLines(maxLines: Int) = apply {
        textView.maxLines = maxLines
    }

    /**
     * 设置为[TextUtils.TruncateAt.MARQUEE]时，需要手动设置
     * [TextView.setHorizontallyScrolling]且设置setSelected(true)
     *
     * @param ellipsize TextUtils.TruncateAt
     * @return ShapedLabelView
     */
    fun setEllipsize(ellipsize: TextUtils.TruncateAt) = apply {
        textView.ellipsize = ellipsize
    }

    fun setTextDrawable(
        @GravityInt gravity: Int,
        drawable: Drawable?,
        width: Int,
        height: Int,
        paddingStart: Int,
        paddingTop: Int,
        paddingEnd: Int,
        paddingBottom: Int
    ) = apply {
        val drawableWidth = if (width == -1) drawable?.intrinsicWidth ?: 0 else width
        val drawableHeight = if (height == -1) drawable?.intrinsicHeight ?: 0 else height
        textView.setDrawable(
            gravity, drawable, drawableWidth, drawableHeight, paddingStart, paddingTop, paddingEnd,
            paddingBottom
        )
    }

    fun createPaddingDrawable(
        drawable: Drawable,
        width: Int,
        height: Int,
        paddingStart: Int,
        paddingTop: Int,
        paddingEnd: Int,
        paddingBottom: Int
    ): Drawable {
        val drawableWidth = if (width == -1) drawable.intrinsicWidth else width
        val drawableHeight = if (height == -1) drawable.intrinsicHeight else height
        val paddingDrawable = LayerDrawable(arrayOf(drawable))
        paddingDrawable.setLayerInset(0, paddingStart, paddingTop, paddingEnd, paddingBottom)
        paddingDrawable.setBounds(
            0, 0, drawableWidth + paddingStart + paddingEnd,
            drawableHeight + paddingTop + paddingBottom
        )
        return paddingDrawable
    }

    override fun setStateType(stateType: StateType) = apply {
        this.stateType = stateType
        setTextColor(textColorNormal, textColorState, textColorDisable)
        setImageResource(imageNormal, imageState, imageDisable)
    }

    private fun Int.toEllipsize(): TextUtils.TruncateAt {
        return when (this) {
            1 -> TextUtils.TruncateAt.START
            2 -> TextUtils.TruncateAt.MIDDLE
            3 -> TextUtils.TruncateAt.END
            4 -> TextUtils.TruncateAt.MARQUEE
            else -> TextUtils.TruncateAt.START
        }
    }
}